package MicroSpring.applicationContext.impl;

import MicroSpring.applicationContext.BeansLifeCycle;
import MicroSpring.applicationContext.ConfigurableApplicationContext;
import MicroSpring.beanFactory.impl.BeanDefinition;
import MicroSpring.beanFactory.impl.DefaultListableBeanFactory;
import MicroSpring.beanFactoryPostProcessor.BeanFactoryPostProcessor;
import MicroSpring.beanFactoryPostProcessor.impl.BootStrapClassPostProcessor;
import MicroSpring.beanFactoryPostProcessor.impl.ConfigurationClassPostProcessor;
import MicroSpring.beanPostProcessor.BeanPostProcessor;
import MicroSpring.beanPostProcessor.DestructionAwareBeanPostProcessor;
import MicroSpring.beanPostProcessor.InstantiationAwareBeanPostProcessor;
import MicroSpring.beanPostProcessor.impl.CommonAnnotationBeanPostProcessor;
import MicroSpring.builder.BeanDefinitionBuilder;
import MicroSpring.builder.BeanNameGenerator;
import MicroSpring.registry.BeanDefinitionRegistry;

import javax.annotation.PreDestroy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static MicroSpring.beanFactory.impl.DefaultListableBeanFactory.SINGLETON;

/*
    抽象容器: 定义了一套refresh的算法骨架
 */
public abstract class AbstractApplicationContext implements  BeansLifeCycle, ConfigurableApplicationContext, BeanDefinitionRegistry {
    private final Object startupShutdownMonitor=new Object();
    protected DefaultListableBeanFactory beanFactory;
    private final BeanNameGenerator beanNameGenerator;
    private final BeanDefinitionBuilder beanDefinitionBuilder;
    private boolean active;
    private boolean running;
    private boolean refreshing;
    public AbstractApplicationContext() {
        this.beanNameGenerator=new BeanNameGenerator();
        this.beanDefinitionBuilder=new BeanDefinitionBuilder();
    }

    @Override
    public void setClassLoader(ClassLoader classLoader) {

    }

    @Override
    public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {

    }

    /**
     *
     * @throws IllegalStateException
     * *
     *   准备bean后处理器,beanFactory后处理器并invoke它们.
     */
    public void refresh() throws  IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            //准备工作: 初始化bean工厂
            this.prepareRefresh();
            this.prepareBeanFactory();

            try {
                // 工厂后处理器的注册以及执行工厂后处理器的动作
                this.registerBeanFactoryPostProcessors();
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //注册bean后处理器,
                this.registerBeanPostProcessors(beanFactory);
                this.prepareBeanPostProcessors(beanFactory);
                this.invokeBeanPostProcessors();
            } catch (Throwable e) {
                //销毁beans
                this.destroyBeans();
                //取消刷新
                this.cancelRefresh();
                throw e;
            } finally {
                refreshEnd();

            }

        }
    }

    private void refreshEnd(){
        this.refreshing=false;
    }

    private void cancelRefresh() {
        this.active=false;
        this.running=false;
    }

    private void destroyBeans() {
        this.beanFactory.destroyBeans();
    }

    private void prepareBeanPostProcessors(DefaultListableBeanFactory beanFactory) {
        //把ImportAwareBeanPostProcessor放在最后添加。
        registerConfigurableBeanBeanProcessor(beanFactory.getBean(ConfigurationClassPostProcessor.class).getImportAwareBeanPostProcessor());
    }

    protected void registerBeanFactoryPostProcessors(){
        if (beanFactory.isBootStrap()){
            final BootStrapClassPostProcessor bootStrapClassPostProcessor=new BootStrapClassPostProcessor();
            registerConfigurableBeanFactoryProcessor(bootStrapClassPostProcessor);
        }

        final ConfigurationClassPostProcessor configurationClassPostProcessor = new ConfigurationClassPostProcessor();
        registerConfigurableBeanFactoryProcessor(configurationClassPostProcessor);

    }

    private void registerConfigurableBeanFactoryProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
        registerConfiturableBean(beanFactoryPostProcessor);
        beanFactory.addBeanFactoryPostProcessor(beanFactoryPostProcessor);
    }
    public void registerConfiturableBean(Object obj){
        BeanDefinition beanDefinition = beanDefinitionBuilder.setClass(obj.getClass()).setScope(SINGLETON).setRepetable(false).build();
        String beanName = beanNameGenerator.generateBeanName(beanDefinition);
        this.registerSingleton(beanName,obj);
        this.registerBeanDefinition(beanName,beanDefinition);
    }

    @Override
    public void registerBeanDefinition(String var1, BeanDefinition var2) {
        this.beanFactory.registerBeanDefinition(var1,var2);
    }

    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        this.beanFactory.registerSingleton(beanName,singletonObject);
    }

    private void invokeBeanFactoryPostProcessors(DefaultListableBeanFactory beanFactory) {
        List<BeanFactoryPostProcessor> beanFactoryPostProcessorList = beanFactory.getBeanFactoryPostProcessorList();
        for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorList) {
            beanFactoryPostProcessor.postProcessBeanDefinitionRegistry(beanFactory);
        }
    }

    // 注册bean后处理器 交由不同的context容器去做
    protected abstract void registerBeanPostProcessors(DefaultListableBeanFactory beanFactory);


    /**
     * * 执行bean处理器,其实这里才是控制bean的生命周期的地方。
     */
    private void invokeBeanPostProcessors() {
        instantiationBeans();
        List<BeanPostProcessor> beanPostProcessorList = beanFactory.getBeanPostProcessorList();

        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = beanFactory.getBeanDefinitionMap();
            ConcurrentHashMap<String, Object> singletonBeanObjects = beanFactory.getSingletonBeanObjects();
            for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
                String beanName = entry.getKey();
                BeanDefinition beanDefinition = entry.getValue();
                if (!beanDefinition.getReplaceable())
                    continue;
                Class beanClazz = beanDefinition.getClazz();

                //1.实例化前: bean处理器 这里返回的对象会替换原本的bean
                Object o1 =singletonBeanObjects.getOrDefault(beanName,null);
                if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor){
                    Object o = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessBeforeInstantiation(beanClazz, beanName);
                    if (o!=null)
                        o1 = o;
                }
                //2.实例化bean
                Object o2 = o1;
                if (o2==null)
                    o2=this.instantiationBean(beanClazz, beanDefinition);

                //3.实例化后：bean处理器处理回false会跳过依赖注入阶段
                boolean b = true;
                if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor){
                    b = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessAfterInstantiation(o2, beanName);
                }

                //5.依赖注入阶段: bean处理器
                if (b) {
                    this.dependcyInject(beanPostProcessor, beanName, o2);
                }
                //6.初始化前: bean处理器处理
                Object o3 = beanPostProcessor.postProcessBeforeInitialization(o2, beanName);
                //7.初始化bean
                this.initializationBean(beanName,o2);
                //8.初始化之后: bean处理器处理
                o3 = beanPostProcessor.postProcessAfterInitialization(o3, beanName);
                //9.销毁之前: CommonAnnotationBeanPostProcessor 标记bean是否需要做销毁前处理
                if (beanPostProcessor instanceof CommonAnnotationBeanPostProcessor){
                    ((DestructionAwareBeanPostProcessor)beanPostProcessor).postProcessBeforeDestruction(o3,beanName);
                }
                //10. 注册bean
                this.registerBean(o3,beanDefinition,beanName);
            }
        }
    }





    public void registerBean(Object obj,BeanDefinition beanDefinition,String beanName){
        this.registerSingleton(beanName,obj);
        this.registerBeanDefinition(beanName,beanDefinition);
    }


    void registerConfigurableBeanBeanProcessor(BeanPostProcessor instantiationAwareBeanPostProcessor) {
        registerConfiturableBean(instantiationAwareBeanPostProcessor);
        beanFactory.addBeanPostProcessor(instantiationAwareBeanPostProcessor);
    }


    /**
     * *
     * @param beanDefinitionMap
     * * 根据beanDefinition进行实例化对象.
     */
    private void defaultInstantiationBeans(ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap) {
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            if (beanFactory.containsBean(entry.getKey())&&!entry.getValue().getReplaceable())
                continue;
            Object instantiationBean = instantiationBean(entry.getValue().getClazz(), entry.getValue());
            beanFactory.registerSingleton(entry.getKey(),instantiationBean);
        }
    }
    private Object instantiationBean(Class beanClazz, BeanDefinition beanDefinition) {
        if (beanFactory.containsBean(beanClazz)&&!beanDefinition.getReplaceable()){
            return null;
        }
        try {
            Object o = beanClazz.newInstance();
            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }


    private void prepareBeanFactory() {
        if (this.beanFactory==null)
            this.beanFactory=new DefaultListableBeanFactory();
    }

    private void prepareRefresh() {
        refreshing=true;
        active=true;
        running=true;
    }


    @Override
    public void close() {
        handlerPreDestroy();
        this.destroyBeans();
    }

    private void handlerPreDestroy(){
        ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = beanFactory.getBeanDefinitionMap();
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            if (entry.getValue().isPredestroy()){
                invokePreDestroy(entry.getKey());
            }
        }
    }

    private void invokePreDestroy(String beanName){
        List<BeanPostProcessor> beanPostProcessorList = beanFactory.getBeanPostProcessorList();
        Object bean = beanFactory.getBean(beanName);
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor
                    && !(beanPostProcessor instanceof CommonAnnotationBeanPostProcessor)){
               ((DestructionAwareBeanPostProcessor)beanPostProcessor)
                       .postProcessBeforeDestruction(bean,beanName);
            }
        }
        for (Method m : bean.getClass().getDeclaredMethods()) {
            if (m.isAnnotationPresent(PreDestroy.class)){
                try {
                    m.invoke(bean);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Override
    public boolean isActive() {
        return active;
    }

    @Override
    public DefaultListableBeanFactory getBeanFactory() {
        return this.beanFactory;
    }

    @Override
    public Collection<String> getBeanDefinitionNames() {
        return beanFactory.getBeanDefinitionNames();
    }

    @Override
    public void instantiationBeans() {
        defaultInstantiationBeans(beanFactory.getBeanDefinitionMap());
    }
    @Override
    public void dependcyInject(BeanPostProcessor beanPostProcessor, String beanName, Object o2) {
        if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
            ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessProperties(o2, beanName);
        }
    }


    @Override
    public void initializationBean(String beanName,Object bean) {

    }

    @Override
    public void destructionBeans() {
        this.destroyBeans();
    }


    public boolean isRunning() {
        return this.running;
    }

    @Override
    public void removeBeanDefinition(String var1) {
        this.beanFactory.removeBeanDefinition(var1);
    }

    @Override
    public BeanDefinition getBeanDefinition(String var1) {
        return null;
    }

    @Override
    public boolean containsBeanDefinition(String var1) {
        return this.beanFactory.containsBeanDefinition(var1);
    }



    @Override
    public int getBeanDefinitionCount() {
        return this.beanFactory.getBeanDefinitionCount();
    }

    @Override
    public boolean isBeanNameInUse(String var1) {
        return this.beanFactory.isBeanNameInUse(var1);
    }
}
